using System;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using SmartBoard.Core.BaseObjects;

namespace SmartBoard.Core.Networking
{
    /// <summary>
    /// Base socket connection
    /// </summary>
    public abstract class BaseSocketConnection : Disposable, ISocketConnection
    {
        #region Fields
        //----- Connection!

        private object _syncData;

        //----- Active!
        private readonly object _syncActive;
        private bool _isActive;

        //----- Event Processing!
        private object _syncEventProcessing;
        private EventProcessing _eventProcessing;

        //----- Connection Host and Creator!

        //----- Socket and Stream!

        //----- Write items!

        //----- Read items!

        #endregion

        #region Ctor

        internal BaseSocketConnection(BaseSocketConnectionHost host, BaseSocketConnectionCreator creator, Socket socket)
        {

            //----- Connection Id!
            ConnectionId = host.GetConnectionId();
            
            _syncData = new object();
            ReadBytes = 0;
            WriteBytes = 0;

            BaseHost = host;
            BaseCreator = creator;
            Socket = socket;

            _syncActive = new object();
            _isActive = false;

            WriteOV = new SocketAsyncEventArgs();
            ReadOV = new SocketAsyncEventArgs();

            WriteQueue = new Queue<MessageBuffer>();
            WriteQueueHasItems = false;

            SyncReadPending = new object();
            ReadPending = false;

            _syncEventProcessing = new object();
            _eventProcessing = EventProcessing.None;

            LastAction = DateTime.Now;

            UserData = null;
            Encryptor = null;
            Decryptor = null;
        }

        #endregion

        #region Destructor

        protected override void Free(bool canAccessFinalizable)
        {

            if (WriteQueue != null)
            {
                WriteQueue.Clear();
                WriteQueue = null;
            }

            if (Stream != null)
            {
                Stream.Close();
                Stream = null;
            }

            if (Decryptor != null)
            {
                Decryptor.Dispose();
                Decryptor = null;
            }

            if (Encryptor != null)
            {
                Encryptor.Dispose();
                Encryptor = null;
            }

            if (ReadOV != null)
            {
                
                var t = typeof(SocketAsyncEventArgs);

                var f = t.GetField("m_Completed", BindingFlags.Instance | BindingFlags.NonPublic);
                f.SetValue(ReadOV, null);

                ReadOV.SetBuffer(null, 0, 0);
                ReadOV.Dispose();
                ReadOV = null;
            }

            if (WriteOV != null)
            {
                var t = typeof(SocketAsyncEventArgs);

                var f = t.GetField("m_Completed", BindingFlags.Instance | BindingFlags.NonPublic);
                f.SetValue(WriteOV, null);

                WriteOV.SetBuffer(null, 0, 0);
                WriteOV.Dispose();
                WriteOV = null;
            }

            if (Socket != null)
            {
                Socket.Close();
                Socket = null;
            }

            BaseHost = null;
            BaseCreator = null;
            SyncReadPending = null;
            _syncData = null;
            _syncEventProcessing = null;

            base.Free(canAccessFinalizable);
        }

        #endregion

        #region Properties

        internal SocketAsyncEventArgs WriteOV { get; private set; }
        internal Queue<MessageBuffer> WriteQueue { get; private set; }
        internal bool WriteQueueHasItems { get; set; }

        internal bool ReadPending { get; set; }
        internal object SyncReadPending { get; private set; }
        internal SocketAsyncEventArgs ReadOV { get; private set; }

        internal object SyncActive
        {
            get { return _syncActive; }
        }

        internal EventProcessing EventProcessing
        {
            get
            {
                lock (_syncEventProcessing)
                {
                    return _eventProcessing;
                }
            }
            set
            {
                lock (_syncEventProcessing)
                {
                    _eventProcessing = value;
                }
            }
        }

        internal bool Active
        {
            get 
            {
                if (IsDisposed)
                {
                    return false;
                }

                lock (_syncActive)
                {
                    return _isActive;
                }
            }
            set 
            {
                lock (_syncActive)
                {
                    _isActive = value;    
                }
            }
        }

        internal ICryptoTransform Encryptor { get; set; }

        internal ICryptoTransform Decryptor { get; set; }

        internal Stream Stream { get; set; }

        internal Socket Socket { get; set; }

        internal byte[] Delimiter
        {
            get
            {
                switch (EventProcessing)
                {
                    case EventProcessing.User:
                        return BaseHost.Delimiter;
                    case EventProcessing.Encrypt:
                        return BaseHost.DelimiterEncrypt;
                    case EventProcessing.Proxy:
                        return null;
                    default:
                        return null;
                }
            }
        }

        internal DelimiterType DelimiterType
        {
            get
            {
                switch (EventProcessing)
                {
                    case EventProcessing.User:
                        return BaseHost.DelimiterType;
                    case EventProcessing.Encrypt:
                        return DelimiterType.MessageTailExcludeOnReceive;
                    case EventProcessing.Proxy:
                        return DelimiterType.None;
                    default:
                        return DelimiterType.None;
                }
            }
        }

        internal EncryptionType EncryptionType
        {
            get { return BaseCreator.EncryptionType; }
        }

        internal CompressionType CompressionType
        {
            get { return BaseCreator.CompressionType; }
        }

        internal HostType HostType
        {
            get { return BaseHost.HostType; }
        }

        internal BaseSocketConnectionCreator BaseCreator { get; private set; }

        internal BaseSocketConnectionHost BaseHost { get; private set; }

        #endregion

        #region Methods

        internal void SetConnectionData(int readBytes, int writeBytes)
        {
            if (IsDisposed) return;

            lock (_syncData)
            {
                if (readBytes > 0)
                {
                    ReadBytes += readBytes;
                }
                if (writeBytes > 0)
                {
                    WriteBytes += writeBytes;
                }
                LastAction = DateTime.Now;
            }
        }

        #endregion

        #region ISocketConnection Members

        #region Properties

        public object UserData { get; set; }

        public IPEndPoint LocalEndPoint
        {
            get { return (IPEndPoint)Socket.LocalEndPoint; }
        }

        public IPEndPoint RemoteEndPoint
        {
            get { return (IPEndPoint)Socket.RemoteEndPoint; }
        }

        public IntPtr SocketHandle
        {
            get { return Socket.Handle; }
        }

        public long ConnectionId { get; private set; }

        public IBaseSocketConnectionCreator Creator
        {
            get { return BaseCreator; }
        }

        public IBaseSocketConnectionHost Host
        {
            get { return BaseHost; }
        }

        public DateTime LastAction { get; private set; }

        public long ReadBytes { get; private set; }

        public long WriteBytes { get; private set; }

        #endregion

        #region Socket Options

        public void SetTTL(short value)
        {
            Socket.Ttl = value;
        }

        public void SetLinger(LingerOption lo)
        {
            Socket.LingerState = lo;
        }

        public void SetNagle(bool value)
        {
            Socket.NoDelay = value;
        }

        #endregion

        public void BeginSend(byte[] buffer)
        {
            if (IsDisposed) return;

            BaseHost.BeginSend(this, buffer, false);
        }

        public void BeginReceive()
        {
            if (IsDisposed) return;

            BaseHost.BeginReceive(this);
        }

        public void BeginDisconnect()
        {
            if (IsDisposed) return;

            BaseHost.BeginDisconnect(this);
        }

        public ISocketConnection[] GetConnections()
        {
            return IsDisposed ? null : BaseHost.GetConnections();
        }

        public ISocketConnection GetConnectionById(long id)
        {
            return IsDisposed ? null : BaseHost.GetSocketConnectionById(id);
        }

        #endregion

        #region Abstract Methods

        public abstract IClientSocketConnection AsClientConnection();
        public abstract IServerSocketConnection AsServerConnection();

        #endregion
    }
}